package com.example.cachedemo;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.util.LruCache;

import com.jakewharton.disklrucache.DiskLruCache;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public final class BitmapCache {
    // 内存缓存
    private LruCache<String, Bitmap> memoryCache;

    // 硬盘缓存
    private DiskLruCache diskCache;

    // 保存外部传入的context
    private Context context;

    // 用类名做本类的TAG
    private static String TAG = BitmapCache.class.getSimpleName();

    // 硬盘缓存目录名字
    private static final String DISK_CACHE_NAME = "DiskBitmapCache";

    // 硬盘缓存大小，10M
    private static final long DISK_CACHE_SIZE = 10 * 1024 * 1024;

    // 磁盘缓存一些固定参数
    private  static final int DISK_CACHE_INDEX = 0;
    private  static final int DISK_CACHE_VALUE_COUNT = 1;

    // 硬盘缓存要求context
    public BitmapCache(Context context) {
        this.context = context.getApplicationContext();
        initMemoryCache();
        initDiskCache(this.context);
    }

    // 无参构造函数，禁止调用
    private BitmapCache() {}

    /**
     * 从缓存中读取Bitmap
     * @param imageUrl  图片url
     * @return          Bitmap；如果内存中没有，返回null
     */
    public Bitmap getBitmap(String imageUrl) {
        Bitmap bitmap = null;
        try {
            String key = EncodeUtil.urlStringToMd5String(imageUrl);
            // 先从内存缓存读取; 如果有，直接返回
            bitmap = memoryCache.get(key);
            if (bitmap != null) {
                Log.v(TAG, "来自内存缓存");
                return bitmap;
            }
            // 内存缓存中没有，从磁盘缓存读取
            bitmap = getBitmapFromDiskCache(imageUrl);
            if (bitmap != null) {
                Log.v(TAG, "来自磁盘缓存");
                return bitmap;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 内存缓存和磁盘缓存中都没有
        return null;
    }

    /**
     * 下载图片并写入硬盘缓存；这个过程耗时，放入子线程执行
     * @param imageUrl   图片url
     * @return           执行结果
     */
    public boolean downloadUrlToDiskCache(String imageUrl) {
        try {
            String key = EncodeUtil.urlStringToMd5String(imageUrl);
            DiskLruCache.Editor editor = diskCache.edit(key);
            if (editor != null) {
                OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
                if (NetworkUtil.downloadUrlToStream(imageUrl, outputStream)) {
                    editor.commit();
                    Log.v(TAG, "下载" + imageUrl + "到磁盘缓存");
                    return true;
                } else {
                    editor.abort();
                    Log.v(TAG, "下载" + imageUrl + "失败");
                    return false;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 默认不成功
        return false;
    }

    /**
     * 清空缓存
     */
    public void clear() {
        // 磁盘缓存
        memoryCache.evictAll();

        // 硬盘缓存
        try {
            diskCache.delete();
            // 清除之后会自动关闭，要重新打开
            initDiskCache(context);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 缓存大小信息
     * @return  信息字符串，Kb
     */
    public String size() {
        // 内存缓存的单位是Kb，由重写的sizeOf方法决定
        int memorySize = memoryCache.size();
        // 磁盘缓存的单位是byte
        long diskSize = diskCache.size();
        return ("内存缓存:" + memorySize + "Kb;" + "磁盘缓存:" + diskSize/1024 + "Kb。");
    }

    /**
     * 删除指定图片
     * @param imageUrl  图片的url
     */
    public void removeImage(String imageUrl) {
        String key = EncodeUtil.urlStringToMd5String(imageUrl);
        if (key == null) {
            return;
        }
        try {
            memoryCache.remove(key);
            diskCache.remove(key);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 仅磁盘缓存在某些情况下需要，比如进入后台
     */
    public void flush() {
        try {
            diskCache.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 从磁盘缓存读入图片，并转存到内存缓存中
     * @param imageUrl 图片url
     * @return Bitmap
     */
    private Bitmap getBitmapFromDiskCache(String imageUrl) {
        Bitmap bitmap = null;
        try {
            String key = EncodeUtil.urlStringToMd5String(imageUrl);
            DiskLruCache.Snapshot snapshot = diskCache.get(key);
            if (snapshot != null) {
                InputStream inputStream = snapshot.getInputStream(DISK_CACHE_INDEX);
                bitmap = BitmapFactory.decodeStream(inputStream);
                // 如果成功读取了，存入内存缓存中
                if (bitmap != null) {
                    memoryCache.put(key, bitmap);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return bitmap;
    }

    /**
     * 初始化内存缓存
     */
    private void initMemoryCache() {
        // 线程总内存，以KB为单位。
        final int maxMemory = (int)Runtime.getRuntime().maxMemory() / 1024;
        // 缓存大小为总内存的1/8，以KB为单位。
        final int cacheSize = maxMemory / 8;
        memoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                // Bitmap, 以KB为单位。
                int size = BitmapUtil.getBitmapSize(value) / 1024;
                return size;
            }
        };
        Log.d(TAG, "initMemoryCache finished");
    }

    /**
     *
     * @param context 上下文
     */
    private void initDiskCache(Context context) {
        File directory = FileUtil.getCacheDirectory(context, DISK_CACHE_NAME);
        int appVersion = AppUtil.getAppVersion(context);
        try {
            diskCache = DiskLruCache.open(directory, appVersion, DISK_CACHE_VALUE_COUNT, DISK_CACHE_SIZE);
            Log.v(TAG, "initDiskCache finished");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
